/********************************************************************************
 * Copyright (c)2016-2020, ShenZhen WanJiaAn Technology CO.,LTD.
 * All Rights Reserved.
 *
 * File Name     : hi_hal_syslink.c
 * Author        : joki.zhu
 * Date          : 2020-07-28
 *
 * Record        :
 * 1.Date        : 2020-07-28
 *   Author      : joki.zhu
 *   Modification: Created file
 ********************************************************************************/
#include "hi_hal_syslink.h"
#include "hi_syslink_cmd.h"
#include "hi_type.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <asm/io.h>
#include "hi_syslink.h"
#include "hi_appcomm.h"
#include "hi_appcomm_log.h"
#include "app_common.h"

/*********************************************************************************************/
#define HI_HAL_SYSLINK_CLK_GROUP_NUM    (8)
#define HI_HAL_SYSLINK_CLK_BIT_NUM      (7)
#define HI_HAL_SYSLINK_CMD_GROUP_NUM    (8)
#define SHI_HAL_SYSLINK_CMD_BIT_NUM     (5)
#define HI_HAL_SYSLINK_INT_GROUP_NUM    (7)
#define HI_HAL_SYSLINK_INT_BIT_NUM      (2)


#define HI_HAL_SYSLINK_REGISTER_NUM             (6)
#define HAL_SYSLINK_LOCK()                      pthread_mutex_lock(&g_tHalSysLinkMutex);
#define HAL_SYSLINK_UNLOCK()                    pthread_mutex_unlock(&g_tHalSysLinkMutex);

#define HI_HAL_SYSLINK_FAILED_MAX_NUM   (1)

/*********************************************************************************************/
typedef struct {
    HI_BOOL isInited;
    HI_U32 moduleId;
    HI_U32 msgId;
    hi_syslink_boot_info bootInfo;
    HI_SYSLINK_WAKEUP_TYPE enWakeupType;
} HAL_SYSLINK_Context;

static HAL_SYSLINK_Context gs_syslinkCtx = {
    .isInited = HI_FALSE,
    .moduleId = HI_APPCOMM_FD_INVALID_VAL,
    .msgId = HI_APPCOMM_FD_INVALID_VAL,
    .bootInfo = { 0 },
    .enWakeupType = HI_SYSLINK_WAKEUP_BUFF,
};

/*********************************************************************************************/
static pthread_mutex_t g_tHalSysLinkMutex = PTHREAD_MUTEX_INITIALIZER;
static HI_HAL_SYSLINK_CALLBACK_FN_PTR g_pfnSysLinkRecvMsgCbk[HI_HAL_SYSLINK_REGISTER_NUM];

/*********************************************************************************************/

static HI_VOID HAL_SYSLINK_StoreBootInfo(hi_syslink_boot_info *info)
{
    gs_syslinkCtx.bootInfo.len = info->len;

    if (info->len < 1) {
        MLOGE("boot info len is 0.\n");
        return;
    }

#if 0
    MLOGP("received boot int,len:%d,msg:", info->len);
    for (HI_U32 index = 0; index < info->len; index++) {
        printf("%c", info->info[index]);
        gs_syslinkCtx.bootInfo.info[index] = info->info[index];
    }
    printf("\n");
#else
    char *wakeupTypeStr[] = {
        "pir",
        "bell key",
        "netcfg key",
        "pick key", 
        "wifi", 
        "rtc", 
        "poweron", 
        "low power", 
        "usb charge", 
        "unknown",
    };
    
    gs_syslinkCtx.enWakeupType = (HI_SYSLINK_WAKEUP_TYPE)info->info[0];
    if ((int)info->info[0] < HI_SYSLINK_WAKEUP_PIR || (int)info->info[0] > HI_SYSLINK_WAKEUP_BUFF) {
        MLOGW("bootinfo[0]: %d is invalid!\n", info->info[0]);
        return ;
    }
    MLOGI("wakeup len:%d type: %s\n", info->len, wakeupTypeStr[gs_syslinkCtx.enWakeupType]);
#endif

    return;
}

static inline HI_VOID HAL_SYSLINK_ProcSyslinkPinMuxGpioEvent(HI_VOID)
{
    MLOGP("system will config syslink gpio mux.\n");
    writel(0x1a00, 0x112c0048); /* gpio8_7 */
    writel(0x1000, 0x112c004C); /* gpio8_5 */

    return;
}

static inline HI_VOID HAL_SYSLINK_ProcSyslinkPinMuxSdioEvent(HI_VOID)
{
    MLOGP("system will config syslink sdio mux.\n");
    writel(0x1D54, 0x112c0048); /* sdio1 clk */
    writel(0x0134, 0x112c0064); /* sdio1 data0 */
    writel(0x0134, 0x112c004C); /* sdio1 cmd */
    writel(0x0000, 0x112C0060); /* sdio1 data1 use as interrupt */

    return;
}

static HI_VOID HAL_SYSLINK_Callback(hi_syslink_callback_type type, HI_VOID *arg)
{
    hi_syslink_callback_args *cbArgs = (hi_syslink_callback_args *)arg;

    switch (type) {
        case HI_SYSLINK_BOOT_INFO: {
            if (cbArgs != HI_NULL) {
                HAL_SYSLINK_StoreBootInfo(&cbArgs->boot_info);
            } else {
                MLOGE("boot info is null.\n");
            }
            break;
        }
        case HI_SYSLINK_PIN_MUX_GPIO: {
            HAL_SYSLINK_ProcSyslinkPinMuxGpioEvent();
            break;
        }
        case HI_SYSLINK_PIN_MUX_SDIO: {
            HAL_SYSLINK_ProcSyslinkPinMuxSdioEvent();
            break;
        }
        case HI_SYSLINK_NET_CHANGED: {
            MLOGP("network changed success.\n");
            break;
        }
        case HI_SYSLINK_NET_CONFLICT: {
            MLOGP("network conflict.\n");
            break;
        }
        case HI_SYSLINK_ESTABLISHED: {
            MLOGP("syslink established with wifi.\n");
            break;
        }
        case HI_SYSLINK_INTER_EXCEPTION: {
            if (cbArgs != HI_NULL) {
                MLOGE("syslink exception: 0x%x.\n", cbArgs->exception);
            } else {
                MLOGE("syslink exception.\n");
            }
            break;
        }
        default:
        {
            break;
        }
    }

    return;
}

static HI_S32 HAL_SYSLINK_FillOkMsgToResopnse(hi_syslink_response *response)
{
    response->resp_len = HAL_SYSLINK_MSG_LEN;
    response->response = malloc(response->resp_len);
    if (response->response == HI_NULL) {
        response->resp_len = 0;
        return HI_FAILURE;
    }
    memset_s(response->response, response->resp_len, 0, response->resp_len);
    HI_S32 ret = snprintf_s(response->response, response->resp_len, response->resp_len - 1, "%s",
                            "ok");
    if (ret < 0) {
        MLOGE("snprintf_s failed\n");
        return HI_FAILURE;
    }

    return HI_SUCCESS;
}

static HI_S32 HAL_SYSLINK_DeinitCamera(HI_U32 argc, HI_CHAR **argv)
{
    MLOGP("received deinit camera msg from wifi ...\n");

    /* just do something in here for deinit camera */
    MLOGP("camera deinit success.\n");

    /* send deinit camera msg to wifi */
    const HI_CHAR *msg = "socstandbyready";
    HI_S32 len = strlen(msg) + 1;
    HAL_SYSLINK_MsgQos qos = HAL_SYSLINK_MSG_QOS_DIRECTLY;
    HI_S32 ret = HI_HAL_SYSLINK_SendTextMsgToWifi(msg, len, qos);
    HI_APPCOMM_LOG_AND_RETURN_IF_FAIL(ret, ret, "HI_SAMPLE_SYSLINK_SendMsgToWifi");
    MLOGP("send soc standby ready msg to wifi.\n");

    return HI_SUCCESS;
}

static inline HI_VOID HAL_SYSLINK_DumpBuf(const HI_CHAR *buf, HI_U32 len)
{
    printf("msg(%d):", len);
    for (HI_U32 index = 0; index < len; index++) {
        printf("%c", buf[index]);
    }
    printf("\n");

    return;
}

typedef struct {
    HI_U8 cmdTitle[32];
    HI_S32 (*cmdFunc)(HI_U32 argc, HI_CHAR **argv);
} SAMPLE_SYSLINK_CmdAttr;

static SAMPLE_SYSLINK_CmdAttr gs_cmdList[] = {
    { "deinitcamera", HAL_SYSLINK_DeinitCamera },
};

static HI_S32 HAL_SYSLINK_FindMatchedCmdAndRun(const hi_syslink_request *request)
{
    MLOGD("received a cmd msg.\n");
    HAL_SYSLINK_DumpBuf((HI_CHAR *)request->request, request->req_len);

    HI_S32 cmdIdx;
    HI_U32 cmdCnt = sizeof(gs_cmdList) / sizeof(SAMPLE_SYSLINK_CmdAttr);
    for (cmdIdx = 0; cmdIdx < cmdCnt; cmdIdx++) {
        if (strcmp(request->request, (HI_CHAR *)gs_cmdList[cmdIdx].cmdTitle) == 0) {
            if (gs_cmdList[cmdIdx].cmdFunc != HI_NULL) {
                gs_cmdList[cmdIdx].cmdFunc(0, HI_NULL);
            }
        }
    }

    return HI_SUCCESS;
}

static HI_S32 HAL_SYSLINK_ProcTextMsg(const hi_syslink_request *request, hi_syslink_response *response)
{
    HI_S32 ret = HAL_SYSLINK_FillOkMsgToResopnse(response);
    HI_APPCOMM_LOG_IF_EXPR_FALSE(ret == HI_SUCCESS, "SAMPLE_SYSLINK_FillOkMsgToResopnse");

    if ((request->req_len <= 0) || (request->request == HI_NULL)) {
        MLOGE("request is invalid:0x%x,%d.\n", request->request, request->req_len);
    } else {
        HAL_SYSLINK_FindMatchedCmdAndRun(request);
    }

    return HI_SUCCESS;
}

static HI_U32 HAL_SYSLINK_MsgCb(HI_U32 msg_id, const hi_syslink_request *request, hi_syslink_response *response)
{
    if ((request == HI_NULL) || (response == HI_NULL)) {
        MLOGE("null point:0x%x,0x%x.\n", request, response);
        return HI_FAILURE;
    }

    int index = 0;
    HI_SYSLINK_PARM_S stRcvMsg;
    
    memset(&stRcvMsg, 0, sizeof(stRcvMsg));
    stRcvMsg.u32MsgId = msg_id;
    memcpy(stRcvMsg.data, request->request, request->req_len);
    stRcvMsg.dataLen = request->req_len;
    
    for (index = 0; index < HI_HAL_SYSLINK_REGISTER_NUM; index++)
    {
        if (NULL != g_pfnSysLinkRecvMsgCbk[index]) {
            g_pfnSysLinkRecvMsgCbk[index](stRcvMsg);
        }
    }

    switch (msg_id) {
        case HI_SYSLINK_MSGID_TEXT: {
            HI_S32 ret = HAL_SYSLINK_ProcTextMsg(request, response);
            HI_APPCOMM_RETURN_IF_FAIL(ret, ret);
            break;
        }
        case HI_SYSLINK_MSGID_W2S_PIR_EVENT: {
            break;
        }
        case HI_SYSLINK_MSGID_W2S_KEY_EVENT: {
            MLOGD("key type: %d isLongPress:%d\n", request->request[0], request->request[1]);
            break;
        }
        case HI_SYSLINK_MSGID_W2S_WIFI_EVENT: {
            MLOGD("wifi event: %d\n", request->request[0]);
            break;
        }
        default:
        {
            MLOGE("unsupported msg_id:0x%x.\n", msg_id);
            break;
        }
    }

    return HI_SUCCESS;
}


static HI_S32 HAL_SYSLINK_SendSyncMsg(const HI_CHAR *msg, HI_S32 len)
{
    hi_syslink_request request;
    hi_syslink_response response;
    HI_U8 resp[HAL_SYSLINK_MSG_LEN] = { 0 };
    request.request = msg;
    request.req_len = len;
    response.response = resp;
    response.resp_len = sizeof(resp);

    HI_U32 timeout = HAL_SYSLINK_MSG_TIMEOUT_MS;
    HI_S32 ret = hi_syslink_send_msg_sync(gs_syslinkCtx.msgId, &request, &response, timeout);
    if (ret != HI_SUCCESS) {
        MLOGE("hi_syslink_send_msg_sync fail ret %d\n", ret);
        return ret;
    }

    if (strcmp(resp, "ok") == 0) {
        return HI_SUCCESS;
    } else {
        return HI_FAILURE;
    }

    return HI_FAILURE;
}

static HI_S32 HAL_SYSLINK_SendAsyncMsg(const HI_CHAR *msg, HI_S32 len)
{
    hi_syslink_request request = { 0 };
    request.request = msg;
    request.req_len = len;
    HI_S32 ret = hi_syslink_send_msg_async(gs_syslinkCtx.msgId, &request);
    HI_APPCOMM_LOG_AND_RETURN_IF_FAIL(ret, ret, "hi_syslink_send_msg_async");

    return ret;
}

static HI_S32 HAL_SYSLINK_SendMsg(const HI_CHAR *msg, HI_S32 len, HAL_SYSLINK_MsgQos qos)
{
    HI_S32 ret;

    if (qos == HAL_SYSLINK_MSG_QOS_WITH_RESP) {
        ret = HAL_SYSLINK_SendSyncMsg(msg, len);
    } else if (qos == HAL_SYSLINK_MSG_QOS_DIRECTLY) {
        ret = HAL_SYSLINK_SendAsyncMsg(msg, len);
    } else {
        ret = HAL_SYSLINK_SendAsyncMsg(msg, len);
    }

    return ret;
}

HI_U32 HI_HAL_SYSLINK_Init(void)
{
    hi_syslink_init_attr init_attr;
    init_attr.sdio_cfg.sdio_dev = 1;
    init_attr.sdio_cfg.sdio_pin.sdio_clk_out.group_number = HI_HAL_SYSLINK_CLK_GROUP_NUM;
    init_attr.sdio_cfg.sdio_pin.sdio_clk_out.bit_number = HI_HAL_SYSLINK_CLK_BIT_NUM;
    init_attr.sdio_cfg.sdio_pin.sdio_cmd.group_number = HI_HAL_SYSLINK_CMD_GROUP_NUM;
    init_attr.sdio_cfg.sdio_pin.sdio_cmd.bit_number = SHI_HAL_SYSLINK_CMD_BIT_NUM;
    init_attr.sdio_cfg.sdio_pin.sdio_int.group_number = HI_HAL_SYSLINK_INT_GROUP_NUM;
    init_attr.sdio_cfg.sdio_pin.sdio_int.bit_number = HI_HAL_SYSLINK_INT_BIT_NUM;
    init_attr.callback = HAL_SYSLINK_Callback;
    HI_S32 ret = hi_syslink_init(&init_attr);
    if (ret != HI_SUCCESS) {
        MLOGE("hi_syslink_init fail ret %d\n", ret);
        return ret;
    }
    gs_syslinkCtx.moduleId = HAL_SYSLINK_MODULE_ID;
    hi_syslink_register_msg_cb(gs_syslinkCtx.moduleId, HAL_SYSLINK_MsgCb);

    gs_syslinkCtx.msgId = HI_SYSLINK_MSGID_TEXT;

//    HI_SYSLINK_CMD_RegOsCmd();

    gs_syslinkCtx.isInited = HI_TRUE;

    MLOGP("hisyslink sucess!\n");

    return HI_SUCCESS;

}

HI_U32 HI_HAL_SYSLINK_Deinit(void)
{
    HI_S32 ret = hi_syslink_deinit();
    if (ret != HI_SUCCESS) {
        MLOGE("hi_syslink_deinit fail ret %d\n", ret);
        return ret;
    }

    hi_syslink_unregister_msg_cb(gs_syslinkCtx.moduleId, HAL_SYSLINK_MsgCb);
    
    return HI_SUCCESS;
}

HI_U32 HI_HAL_SYSLINK_SendMsgToWifiAsync(HI_SYSLINK_PARM_S *pSendMsg)
{
    hi_syslink_request request = { 0 };

    request.request = pSendMsg->data;
    request.req_len = pSendMsg->dataLen;
   
    HI_S32 ret = hi_syslink_send_msg_async(pSendMsg->u32MsgId, &request);
    HI_APPCOMM_LOG_AND_RETURN_IF_FAIL(ret, ret, "hi_syslink_send_msg_async");

    return ret;
}

HI_U32 HI_HAL_SYSLINK_SendMsgToWifiSync(HI_SYSLINK_PARM_S *pSendMsg, HI_SYSLINK_PARM_S *pWaitMsg, HI_U32 timeoutMs)
{
    static char errCnt = 0;
    hi_syslink_request request;
    hi_syslink_response response;
    HI_U8 resp[HAL_SYSLINK_MSG_LEN] = { 0 };

    request.request = pSendMsg->data;
    request.req_len = pSendMsg->dataLen;
    response.response = pWaitMsg->data;
    response.resp_len = pWaitMsg->dataLen;
    
    HI_U32 timeout = timeoutMs;
    HI_S32 ret = hi_syslink_send_msg_sync(pSendMsg->u32MsgId, &request, &response, timeout);
    if (ret != HI_SUCCESS) {
        MLOGE("hi_syslink_send_msg_sync fail ret %d\n", ret);
        if (++errCnt >= HI_HAL_SYSLINK_FAILED_MAX_NUM) {
            wifisoc_set_reset("HI_HAL_SYSLINK_SendMsgToWifiSync failed");
        }
        return ret;
    }
    errCnt = 0;

    return ret;
}

HI_U32 HI_HAL_SYSLINK_SendTextMsgToWifi(const HI_CHAR *msg, HI_S32 len, HAL_SYSLINK_MsgQos qos)
{
    HI_S32 ret = HAL_SYSLINK_SendMsg(msg, len, qos);

    return ret;
}

HI_U32 HI_HAL_SYSLINK_GetBootInfo(void)
{
    hi_syslink_boot_info bootInfo = {0};
    HI_S32 ret = hi_syslink_get_boot_info(&bootInfo);
    if (ret == HI_SUCCESS) {
        printf("\n\nget boot info success:\n");
        HAL_SYSLINK_DumpBuf((HI_CHAR *)bootInfo.info, (HI_U32)bootInfo.len);
    } else {
        MLOGE("\n\nget boot info failed(0x%x).\n", ret);
    }

    return ret;
}

HI_U32 HI_HAL_SYSLINK_GetWakeupType(HI_SYSLINK_WAKEUP_TYPE *penWakeupType)
{
    AV_RETURN_IF_PTR_NULL(penWakeupType, HI_FAILURE);
    
    hi_syslink_boot_info bootInfo = {0};
    HI_S32 ret = hi_syslink_get_boot_info(&bootInfo);
    if (ret == HI_SUCCESS) {
        printf("\n\nget boot info success:\n");
        HAL_SYSLINK_DumpBuf((HI_CHAR *)bootInfo.info, (HI_U32)bootInfo.len);
    } else {
        MLOGE("\n\nget boot info failed(0x%x).\n", ret);
    }

    *penWakeupType = (HI_SYSLINK_WAKEUP_TYPE)bootInfo.info[0];

    return ret;
}

int HI_HAL_SYSLINK_Register(HI_HAL_SYSLINK_CALLBACK_FN_PTR pfnRecvMsgCbk)
{
    int index = 0;

    for (index = 0; index < HI_HAL_SYSLINK_REGISTER_NUM; index++)
    {
        if (NULL == g_pfnSysLinkRecvMsgCbk[index]) {
            break;
        } else if (pfnRecvMsgCbk == g_pfnSysLinkRecvMsgCbk[index]) {
            LOGE("This function has been registered !\n");
            return -1;
        }
    }
    
    HAL_SYSLINK_LOCK();
    if (index >= 0 && index < HI_HAL_SYSLINK_REGISTER_NUM)
    {
        g_pfnSysLinkRecvMsgCbk[index] = pfnRecvMsgCbk;
    }
    HAL_SYSLINK_UNLOCK();
    
    return 0;
}


int HI_HAL_SYSLINK_UnRegister(HI_HAL_SYSLINK_CALLBACK_FN_PTR pfnRecvMsgCbk)
{
    int index = 0;

    HAL_SYSLINK_LOCK();
    for (index = 0; index < HI_HAL_SYSLINK_REGISTER_NUM; index++)
    {
        if (pfnRecvMsgCbk == g_pfnSysLinkRecvMsgCbk[index]) 
        {
            g_pfnSysLinkRecvMsgCbk[index] = NULL;
            break;
        }
    }
    HAL_SYSLINK_UNLOCK();

    return 0;
}

